package io.github.openfeign.soap;

import feign.*;
import feign.codec.EncodeException;
import feign.jaxb.JAXBContextFactory;
import feign.soap.SOAPDecoder;
import feign.soap.SOAPEncoder;
import okhttp3.OkHttpClient;
import org.w3c.dom.Document;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class SoapTest2 {

    public interface OpenCard {

        @RequestLine("POST /rgjy/libwebservice.asmx")
        @Headers({"Content-Type:text/xml; charset=utf-8", "SOAPAction:http://tempuri.org/SetReaderState2N"})
        Resp setReaderState2N(UserInfo ui);
    }

    @XmlRootElement(name = "SetReaderState2NResponse",namespace = "http://tempuri.org/")
    public static class Resp {

        private String SetReaderState2NResult;

        @XmlElement(name = "SetReaderState2NResult",namespace = "http://tempuri.org/")
        public String getSetReaderState2NResult() {
            return SetReaderState2NResult;
        }

        public void setSetReaderState2NResult(String setReaderState2NResult) {
            SetReaderState2NResult = setReaderState2NResult;
        }
    }
    @XmlRootElement(name = "SetReaderState2N",namespace = "http://tempuri.org/")
    static class UserInfo {
        String certNo;
        int certType;

        @XmlElement(name = "certNo",namespace = "http://tempuri.org/")
        public String getCertNo() {
            return certNo;
        }

        public void setCertNo(String certNo) {
            this.certNo = certNo;
        }

        @XmlElement(name = "certType",namespace = "http://tempuri.org/")
        public int getCertType() {
            return certType;
        }

        public void setCertType(int certType) {
            this.certType = certType;
        }

        public UserInfo(String certNo, int certType) {
            this.certNo = certNo;
            this.certType = certType;
        }

        public UserInfo() {
        }
    }

    static class CEncoder extends  SOAPEncoder{
        private final JAXBContextFactory jaxbContextFactory;

        public CEncoder(JAXBContextFactory jaxbContextFactory) {
            super(jaxbContextFactory);
            this.jaxbContextFactory = jaxbContextFactory;
        }

        @Override
        public void encode(Object object, Type bodyType, RequestTemplate template) {
            if (!(bodyType instanceof Class)) {
                throw new UnsupportedOperationException(
                        "SOAP only supports encoding raw types. Found " + bodyType);
            }
            try {
                Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
                Marshaller marshaller = jaxbContextFactory.createMarshaller((Class<?>) bodyType);
                marshaller.marshal(object, document);
                SOAPMessage soapMessage = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL).createMessage();
                soapMessage.setProperty(SOAPMessage.WRITE_XML_DECLARATION,
                        Boolean.toString(true));

                soapMessage.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "utf-8");
                QName qname = new QName("http://tempuri.org/", "Muse65WSSoapHeader", "");
                SOAPHeaderElement he = soapMessage.getSOAPHeader().addHeaderElement(qname);
                SOAPElement key = he.addChildElement("IN_WSKEY", "");
                SOAPElement token = he.addChildElement("IN_TOKEN", "");
                key.addTextNode("NISOFT");
                String encrypted = computeToken("NISOFT");
                token.addTextNode(encrypted);

                soapMessage.getSOAPBody().addDocument(document);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                soapMessage.writeTo(bos);
                template.body(new String(bos.toByteArray()));
            } catch (Exception e) {
                throw new EncodeException(e.toString(), e);
            }
        }
    }
    public static void main(String[] args) {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(1, TimeUnit.SECONDS)
                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8089)))
                .build();
        JAXBContextFactory jaxbFactory = new JAXBContextFactory.Builder()
                .withMarshallerJAXBEncoding("UTF-8")
                .build();
        SOAPEncoder encoder = new CEncoder(jaxbFactory);
        SOAPDecoder decoder = new SOAPDecoder.Builder().withJAXBContextFactory(jaxbFactory).build();
        OpenCard client = Feign.builder()
                .client(new feign.okhttp.OkHttpClient(okHttpClient))
                .encoder(encoder)
                .decoder(decoder)
                .target(OpenCard.class, "http://61.163.69.98/");
        UserInfo ui = new UserInfo("20200002", 1);
        Resp resp = client.setReaderState2N(ui);
        System.out.println(resp.SetReaderState2NResult);
    }
    static String computeToken(String key) {
        String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
        String orig = key + dateStr;
        String md51 = md5encrypt(orig).toUpperCase();
        String part1 = md51.substring(0,8);
        String part2 = md51.substring(8);
        String total = part2 + part1;
        String mdt_2 = md5encrypt(total).toUpperCase();
        return mdt_2;
    }

    public static String md5encrypt(String dataStr) {
        try {
            MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(dataStr.getBytes("UTF8"));
            byte s[] = m.digest();
            String result = "";
            for (int i = 0; i < s.length; i++) {
                result += Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }

}
